#ifndef VDSDEVICE_P_WIN_H
#define VDSDEVICE_P_WIN_H

#include <QDebug>
#include <visa.h>
#ifndef WIN32
#include <fcntl.h>
#include <unistd.h>
#endif

#define MAX_BUFFER_LEN 1024 * 1024

class VdsDevicePrivate
{
public:
    VdsDevicePrivate() : m_status(VI_SUCCESS), m_connected(false) { }

    bool open(const char *connStr);
    bool close();

    bool enumDevice();
    bool connect(const char *connStr);

    int read(unsigned char *buf, size_t length);
    bool write(const char * buf, size_t length);

    long errorCode() const;

private:
    ViSession m_defaultRM;
    ViSession m_instr;
    ViStatus m_status;

    ViUInt32 m_numInstrs;
    ViFindList m_findList;

    char m_instrResourceString[VI_FIND_BUFLEN];
    unsigned char m_readBuffer[MAX_BUFFER_LEN];

    bool m_connected;

    int fd;
};

long VdsDevicePrivate::errorCode() const
{
    return m_status;
}

bool VdsDevicePrivate::enumDevice()
{
#ifdef WIN32
    m_status = viOpenDefaultRM(&m_defaultRM);
    if (m_status < VI_SUCCESS)
    {
        qDebug("Error: Could not open a session to the VISA Resource Manager!");
        return false;
    }

    m_status = viFindRsrc(m_defaultRM, "USB?*INSTR", &m_findList, &m_numInstrs, m_instrResourceString);
    if (m_status < VI_SUCCESS)
    {
        qDebug("Error: Could not locate the resources associated to the VISA Resource Manager!");
        viClose (m_defaultRM);
        return false;
    }

    for (size_t i = 0; i < m_numInstrs; i++)
    {
        qDebug("%d\t%s", i, m_instrResourceString);
    }

#endif

    return true;
}

bool VdsDevicePrivate::connect(const char * connStr)
{
#ifdef WIN32
    for (size_t i = 0; i < m_numInstrs; i++)
    {
        if (QString(m_instrResourceString).contains(connStr))
        {
            m_status = viOpen(m_defaultRM, m_instrResourceString, VI_NULL, VI_NULL, &m_instr);
            if (m_status < VI_SUCCESS)
            {
                qDebug("Error: Cannot open a session to the device: %s.", m_instrResourceString);
            }
            else
            {
                qDebug("Open a session to the device %s success.", m_instrResourceString);
                m_connected = true;
                return true;
            }
        }

        viFindNext(m_findList, m_instrResourceString);
    }

    viClose(m_defaultRM);
    return false;
#else
    m_status = fd = ::open(connStr, O_RDWR);
    if (m_status < VI_SUCCESS)
    {
        qDebug("Error: Cannot open a session to the device: %s.", connStr);
        return false;
    }
    else
    {
        qDebug("Open a session to the device %s success.", connStr);
        m_connected = true;
        return true;
    }
#endif
}

bool VdsDevicePrivate::open(const char * connStr)
{
    return enumDevice() && connect(connStr);
}

bool VdsDevicePrivate::close()
{
    if (!m_connected)
    {
        return true;
    }

#ifdef WIN32
    m_status = viClose(m_defaultRM);
#else
    m_status = ::close(fd);
#endif
    if (m_status < VI_SUCCESS)
    {
        qDebug("Error: Cannot close a session to the device: %s.", m_instrResourceString);
        return false;
    }

    qDebug("Close a session to the device %s success.", m_instrResourceString);
    m_connected = false;
    fflush(stdin);
    return true;
}

bool VdsDevicePrivate::write(const char * buf, size_t length)
{
    if (!m_connected)
    {
        qDebug("Error: Device %d disconnected.", 1);
        return false;
    }

    ViUInt32 retCount;

#ifdef WIN32
    m_status = viWrite(m_instr, (ViBuf)buf, (ViUInt32)length, &retCount);
#else
    m_status = retCount = ::write(fd, buf, length);
#endif
    if (m_status < VI_SUCCESS)
    {
        qDebug("Error: Device %d writing data fail.", 1);
        return false;
    }

    qDebug("\nDevice %d (Write): %s\n", 1, buf);

    if (length != retCount)
    {
        qDebug("\nError: Device %d writing data length %d < %d.", 1, retCount, length);
        return false;
    }

    return true;
}

int VdsDevicePrivate::read(unsigned char * buf, size_t length)
{
    if (!m_connected)
    {
        qDebug("Error: device %d disconnected.", 1);
        return -1;
    }

    ViUInt32 retCount;

#ifdef WIN32
    m_status = viRead(m_instr, m_readBuffer, (ViUInt32)length, &retCount);
#else
    m_status = retCount = ::read(fd, m_readBuffer, length);
#endif
    if (m_status < VI_SUCCESS)
    {
        qDebug("Error: read from the device %d.", 1);
        return -2;
    }

    memcpy(buf, m_readBuffer, retCount * sizeof(char));

    qDebug("Device %d (Read): length = %d", 1, retCount);

#if 0
    QString dataStr;
    for (size_t i = 0; i < retCount; ++i)
    {
        dataStr += QString("%1 ").arg(buf[i], 2, 16, QLatin1Char('0'));
    }

    qDebug() << dataStr;
#endif
    return (int)retCount;
}

#endif // VDSDEVICE_P_WIN_H
